`timescale 1ns / 1ps
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Company:			Microsystems Design Lab (MDL)
//					The Pennsylvania State University
// Engineer: 		Yang Xiao
// 
// Create Date:		10/23/2012 
// Design Name: 	scratchpad_mem
// Module Name:     scratchpad_mem
// Project Name:	ISCA-2013
// Target Devices: 
// Tool versions: 
// Description:		A scratchpad memory can be used as cache
//					
//			
//			
//		
//		
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

module scratchpad_mem(
	//----------------------------
	// Inputs
	//----------------------------
	clk					,
	config_en			,		// --- config enable signal ---
	config_mode			,		// --- config mode: 0 means BUFFER, 1 means CACHE ---
	new_frame			,		// --- new frame indicator ---
	new_config			,		// --- new config ---
	image_height		,		// --- image height ---
	image_width			,		// --- image width ---
	src_valid_in		,		// --- image pixel valid signal ---
	data_in				,		// --- image pixel ---
	s_rden				,		// --- buffer read enable ---
	s_row				,		// --- buffer read row, row starts from 0 ---
	s_col				,		// --- buffer read col, col starts from 0 ---

	c_config_en			,		// --- cache config indicator ---
	c_id				,		// --- cache id ---
	c_wren				,		// --- cache write enable ---
	c_din				,		// --- cache data in ---
	c_addr				,		// --- cache address ---
	c_rden				,		// --- cache read enable ---
	//----------------------------
	// Outputs
	//----------------------------
	s_dout				,		// --- buffer data out ---
	s_dout_valid		,		// --- buffer data out valid ---
	
	c_dout				,		// --- cache data out ---
	c_dout_valid				// --- cache data out valid ---
);

	//--------------------------
	// Parameters
	//--------------------------
	parameter	S_DATA_WIDTH	= 20	;		// --- scratch memory data width ---
	parameter	S_NUM_ROWS		= 2		;		// --- scratch memory row numbers ---
	parameter	S_NUM_COLS		= 4		;		// --- scratch memory col numbers ---
	parameter	C_NUM_BYTES		= 5		;		// --- number of bytes in a cache line ---

	// --- global row counter width ---
	parameter	GRC_WIDTH		=	(S_NUM_ROWS <= 2)	 ? 1  :
									(S_NUM_ROWS <= 4)    ? 2  :
									(S_NUM_ROWS <= 8)    ? 3  :
									(S_NUM_ROWS <= 16)   ? 4  :
									(S_NUM_ROWS <= 32)   ? 5  :
									(S_NUM_ROWS <= 64)   ? 6  :
									(S_NUM_ROWS <= 128)  ? 7  :
									(S_NUM_ROWS <= 256)  ? 8  :
									(S_NUM_ROWS <= 512)  ? 9  :
									(S_NUM_ROWS <= 1024) ? 10 :
									(S_NUM_ROWS <= 2048) ? 11 :
									(S_NUM_ROWS <= 4096) ? 12 :
									(S_NUM_ROWS <= 8192) ? 13 : 14;
	// --- global col counter width ---
	parameter	GCC_WIDTH		= 	(S_NUM_COLS <= 2) 	 ? 1  :
									(S_NUM_COLS <= 4)    ? 2  :
									(S_NUM_COLS <= 8)    ? 3  :
									(S_NUM_COLS <= 16)   ? 4  :
									(S_NUM_COLS <= 32)   ? 5  :
									(S_NUM_COLS <= 64)   ? 6  :
									(S_NUM_COLS <= 128)  ? 7  :
									(S_NUM_COLS <= 256)  ? 8  :
									(S_NUM_COLS <= 512)  ? 9  :
									(S_NUM_COLS <= 1024) ? 10 :
									(S_NUM_COLS <= 2048) ? 11 :
									(S_NUM_COLS <= 4096) ? 12 :
									(S_NUM_COLS <= 8192) ? 13 : 14;
	
	//--------------------------
	// Input Ports
	//--------------------------
	input								clk				;
	input								config_en		;
	input								config_mode		;
	input								new_frame		;
	input								new_config		;
	input	[GRC_WIDTH : 0]				image_height	;
	input	[GCC_WIDTH : 0]				image_width		;
	input								src_valid_in	;
	input	[S_DATA_WIDTH-1 : 0]		data_in			;
	input								s_rden			;
	input	[GRC_WIDTH-1 : 0]			s_row			;
	input	[GCC_WIDTH-1 : 0]			s_col			;

	input								c_config_en		;
	input	[31 : 0]					c_id			;
	input								c_wren			;
	input	[C_NUM_BYTES*8-1 : 0]		c_din			;
	input	[GCC_WIDTH-1 : 0]			c_addr			;
	input								c_rden			;
	//--------------------------
	// Output Ports
	//--------------------------
	output	[S_DATA_WIDTH-1 : 0]		s_dout			;
	output								s_dout_valid	;
	
	output	[C_NUM_BYTES*8-1 : 0]		c_dout			; 
	output								c_dout_valid	;
	
	//--------------------------
	// Bidirectional Ports
	//--------------------------	
	
	///////////////////////////////////////////////////////////////////
	// Begin Design
	///////////////////////////////////////////////////////////////////	
	//-------------------------------------------------
	// Signal Declarations: reg
	//-------------------------------------------------
	// ---- Scratchpad memory registers ----
	reg										config_mode_r					= 0	;
	reg		[GRC_WIDTH : 0]					image_height_r					= 0	;	
	reg		[GCC_WIDTH : 0]					image_width_r					= 0	;	
	reg		[GRC_WIDTH-1 : 0]				global_row_r    				= 0 ;
	reg		[GCC_WIDTH-1 : 0]				global_col_r    				= 0 ;
	reg										dst_rdy_in_r    				= 0	;    
    reg		[GCC_WIDTH-1 : 0]       		iib_addra_r[S_NUM_ROWS-1 : 0]		;
    reg		[GCC_WIDTH-1 : 0]       		iib_addrb_r[S_NUM_ROWS-1 : 0]		;
    reg		[S_NUM_ROWS-1 : 0]				iib_wea_r						= 0	;
    reg	    [S_NUM_ROWS-1 : 0]				iib_web_r    					= 0	;
	reg    	[S_DATA_WIDTH*S_NUM_ROWS-1 : 0]	iib_dina_r                  	= 0	;
	reg    	[S_DATA_WIDTH*S_NUM_ROWS-1 : 0]	iib_dinb_r                  	= 0	;
	
	reg		[GRC_WIDTH-1 : 0]				s_row_r[1 : 0]						;
	reg		[GCC_WIDTH-1 : 0]				s_col_r[1 : 0]						;
	reg		[1 : 0]							s_dout_valid_r					= 0	;

	// Reg initialization
	integer i;
	initial begin
		for (i = 0; i < S_NUM_ROWS; i = i+1) begin
			iib_addra_r[i] 		= 0;
			iib_addrb_r[i] 		= 0;
		end
		for (i = 0; i < 2; i = i+1) begin
			s_row_r[i] 		= 0;
			s_col_r[i] 		= 0;
		end
	end

	// ---- Cache registers ----
	reg		[31 : 0]						c_id_r			  		= 0	;
    reg		[GCC_WIDTH*S_NUM_ROWS-1 : 0]  	c_addra_r		  		= 0	;
    reg		[GCC_WIDTH*S_NUM_ROWS-1 : 0]   	c_addrb_r		  		= 0	;
    reg		[S_NUM_ROWS-1 : 0]				c_wea_r			  		= 0	;
	reg    	[S_DATA_WIDTH*S_NUM_ROWS-1 : 0]	c_dina_r          		= 0	;
	reg		[1 : 0]							c_dout_valid_r			= 0	;
	
	//-------------------------------------------------
	// Signal Declarations: wire
	//-------------------------------------------------
	wire   	[S_DATA_WIDTH*S_NUM_ROWS-1 : 0]	iib_dina_w                 	;				// Integral image data input for port A
	wire	[GCC_WIDTH*S_NUM_ROWS-1 : 0]	iib_addra_w					;				// Integral image address bus for port A
	wire	[S_NUM_ROWS-1 : 0]				iib_wea_w					;				// Integral image write enable bus for port A

	wire	[GCC_WIDTH*S_NUM_ROWS-1 : 0]	iib_addrb_w					;				// Integral image address bus for port B
	wire	[S_DATA_WIDTH*S_NUM_ROWS-1 : 0]	iib_doutb_w					;				// Integral image buffer data out bus for port B
	wire	[S_DATA_WIDTH-1 : 0]			doutb_w[S_NUM_ROWS-1 : 0]	;				// Buffer data bus B

    //---------------------------------------------------------------
	// Assignments
	//---------------------------------------------------------------
	generate
		genvar j;	
		for (j = 0; j < S_NUM_ROWS; j = j + 1) begin : BUFFER_ADDR_BUS
			assign iib_addra_w[(j+1)*GCC_WIDTH-1 : j*GCC_WIDTH]		= (~config_mode_r) ? iib_addra_r[j] : c_addra_r[j];
			assign iib_addrb_w[(j+1)*GCC_WIDTH-1 : j*GCC_WIDTH]		= (~config_mode_r) ? iib_addrb_r[j] : c_addrb_r[j];
			assign iib_dina_w	= (~config_mode_r) ? iib_dina_r : c_dina_r;
			assign iib_wea_w	= (~config_mode_r) ? iib_wea_r : c_wea_r;

			assign doutb_w[j]	= iib_doutb_w[(j+1)*S_DATA_WIDTH-1 : j*S_DATA_WIDTH];
		end
	endgenerate

	assign	s_dout 			= (~config_mode_r) ? doutb_w[s_row_r[1]] : 0;
	assign	s_dout_valid 	= s_dout_valid_r[1];	
	assign	c_dout			= (config_mode_r) ? iib_doutb_w : 0; 
	assign	c_dout_valid 	= c_dout_valid_r[1];	
    //---------------------------------------------------------------
	// Instantiations
	//---------------------------------------------------------------
	integral_image_buffer #(
		.PXL_WIDTH(S_DATA_WIDTH)	,
		.NUM_ROWS(S_NUM_ROWS)		,
		.NUM_COLS(S_NUM_COLS)
	)
	integral_image_buffer_inst (
		.clk		(clk				),
		.addra		(iib_addra_w		),
		.wea		(iib_wea_w			),
		.dina		(iib_dina_w			),
		.douta		(					),
		.addrb		(iib_addrb_w		),
		.web		(iib_web_r			),
		.dinb		(iib_dinb_r			),
		.doutb		(iib_doutb_w		)
	);

	//---------------------------------------------------------------
	// Sequential Logic
	//---------------------------------------------------------------
	integer row_index;
	always @(posedge clk) begin
		// ---- Default buffer registers setting ----
		config_mode_r									<= config_mode_r;
		image_height_r									<= image_height_r;
		image_width_r									<= image_width_r;
		global_row_r                   					<= global_row_r;
		global_col_r                   					<= global_col_r;
		dst_rdy_in_r                    				<= dst_rdy_in_r;
        for (row_index = 0; row_index < S_NUM_ROWS; row_index = row_index + 1) begin
            iib_addra_r[row_index]						<= 0;
            iib_addrb_r[row_index]    					<= 0;
        end
        iib_wea_r       								<= {S_NUM_ROWS{1'b0}};
        iib_web_r       								<= {S_NUM_ROWS{1'b0}};
        iib_dina_r      								<= 0;
        iib_dinb_r      								<= 0;
		s_row_r[0]										<= 0;
		s_col_r[0]										<= 0;
		s_row_r[1]										<= s_row_r[0];
		s_col_r[1]										<= s_col_r[0];
		s_dout_valid_r[0]								<= 0;
		s_dout_valid_r[1]								<= s_dout_valid_r[0];

		// ---- Default cache registers setting ----
		c_id_r											<= c_id_r;
		c_addra_r										<= 0;
		c_wea_r	 										<= 0;
		c_dina_r 										<= 0;
		c_addrb_r										<= 0;
		c_dout_valid_r[0]								<= 0;
		c_dout_valid_r[1]								<= c_dout_valid_r[0];

		// -------------------
		// Logic starts here
		// -------------------
		// set the mode
		if (config_en) begin
			config_mode_r 								<= config_mode;
		end

		// --- used as buffer ---
		if (~config_mode_r) begin
			// buffer configuration
			if (new_config) begin
				image_height_r							<= image_height;
				image_width_r							<= image_width;
			end

			// reset pointer for writing for new frame goes high
			if (new_frame) begin
				global_row_r                   		<= 0;
				global_col_r                   		<= 0;
				dst_rdy_in_r                    	<= 1;
				for (row_index = 0; row_index < S_NUM_ROWS; row_index = row_index + 1) begin
					iib_addra_r[global_row_r]			<= global_col_r;
				end
			end

			// write data to buffer when the valid data comes in
			if (src_valid_in && dst_rdy_in_r) begin
				// Update global counter
				if (global_row_r == image_height_r-1 && global_col_r == image_width_r-1) begin
					dst_rdy_in_r                    	<= 0;
        			iib_wea_r       					<= 1 << global_row_r;
					iib_addra_r[global_row_r]			<= global_col_r;
					iib_dina_r							<= {S_NUM_ROWS{data_in}};
				end
				else if (global_col_r == image_width_r-1) begin
        			iib_wea_r       					<= 1 << global_row_r;
					iib_addra_r[global_row_r]			<= global_col_r;
					iib_dina_r							<= {S_NUM_ROWS{data_in}};
					global_row_r						<= global_row_r + 1;
					global_col_r						<= 0;
				end
				else begin
        			iib_wea_r       					<= 1 << global_row_r;
					iib_addra_r[global_row_r]			<= global_col_r;
					iib_dina_r							<= {S_NUM_ROWS{data_in}};
					global_col_r						<= global_col_r + 1;
				end
			end

			// read data from buffer
			if (s_rden) begin
				s_dout_valid_r[0]						<= s_rden;
				s_row_r[0]								<= s_row;
				s_col_r[0]								<= s_col;
				iib_addrb_r[s_row]						<= s_col;
			end

		end
		
		// --- used as cache ---
		else begin
			dst_rdy_in_r                    			<= 0;

			// cache configuration
			if (c_config_en) begin
				c_id_r									<= c_id;
			end
			// cache write
			if (c_wren) begin
				c_addra_r								<= {S_NUM_ROWS{c_addr}};
				c_wea_r									<= {S_NUM_ROWS{1'b1}};
				c_dina_r								<= c_din; 
			end
			// cache read
			if (c_rden) begin
				c_dout_valid_r[0]						<= c_rden;
				c_addrb_r								<= {S_NUM_ROWS{c_addr}};
			end
		end

	end

endmodule 
